package uy.org.softtech.cssuite.web.comando;

import static org.springframework.util.Assert.isTrue;
import static org.springframework.util.Assert.notNull;
import static org.springframework.util.StringUtils.hasText;
import static uy.org.softtech.cssuite.helpers.HelperPassword.validaPassword;
import static uy.org.softtech.cssuite.helpers.HelperTextos.*;

import java.util.Locale;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;

import org.apache.log4j.Logger;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;

import uy.org.softtech.cssuite.core.Comando;
import uy.org.softtech.cssuite.core.Controlador.Accion;
import uy.org.softtech.cssuite.core.Modelo;
import uy.org.softtech.cssuite.entidad.usuario.Usuario;
import uy.org.softtech.cssuite.excepciones.EntidadNoActualizadaException;
import uy.org.softtech.cssuite.excepciones.EntidadNoEncontradaException;
import uy.org.softtech.cssuite.servicios.ServiciosUsuarios;
import uy.org.softtech.cssuite.web.modelo.ModeloPerfil;

/**
 * Implementación de {@link Comando} encargado de procesar la solicitud de actualización del perfil del usuario
 * 
 * @author fabian.lobo
 * @see Comando#ejecutar(Modelo, BindingResult)
 */
@Component("comando-perfil")
public class ComandoPerfil extends Comando {
	
	private Logger log = Logger.getLogger(getClass());
	
	@Resource(name = "servicios-usuarios" )
	private ServiciosUsuarios serviciosUsuarios;
	
	@Resource(name = "codificador-password")
	private PasswordEncoder codificadorPassword;
	
	/**
	 * Método para verificar que todos los parámetros han sido seteados correctamente.
	 * 
	 * @author fabian.lobo
	 */
	@PostConstruct
	public void inicializacion(){
		super.inicializacion();
		
		notNull(serviciosUsuarios(), "La propiedad [serviciosUsuarios] de [" + getClass().getName() + "] no ha sido establecida.");
		notNull(codificadorPassword(), "La propiedad [codificadorPassword] de [" + getClass().getName() + "] no ha sido establecida.");
		
		log.info("Comando encargado de manejar los cambios de credenciales de las cuentas de usuario ha sido inicializado.");
	}
	
	@Override
	public void validaModelo(Modelo modelo, BindingResult errores) {
		notNull(modelo, "El modelo no puede ser nulo.");
		isTrue(modelo instanceof ModeloPerfil, "El modelo no es el correcto para este comando.");
		
		ModeloPerfil modeloPerfil = (ModeloPerfil) modelo;
		
		notNull(modeloPerfil.getAccion(), "Accion no puede ser nulo en este modelo.");
		notNull(modeloPerfil.getLocale(), "Locale no puede ser nulo en este modelo.");
		
		Locale locale = modeloPerfil.getLocale();
		Accion accion = modeloPerfil.getAccion();
		
		//CAMPOS NO EDITABLES -> ID
		notNull(modeloPerfil.getId(), "ID no puede ser nulo en este modelo.");
					
		//CAMPOS EDITABLES REQUERIDOS -> PRIMER NOMBRE, APELLIDO
		String primerNombre = modeloPerfil.getPrimerNombre();
		String apellido = modeloPerfil.getApellido();
		
		//CAMPOS EDITABLES OPCIONALES -> SEGUNDO NOMBRE, CORREO ELECTRONICO, TELEFONO, CELULAR
		String segundoNombre = modeloPerfil.getSegundoNombre();
		String correoElectronico = modeloPerfil.getCorreoElectronico();
		String telefono = modeloPerfil.getTelefono();
		String celular = modeloPerfil.getCelular();
		
		//CAMBIO DE CREDENCIALES
		String password = modeloPerfil.getPassword();
		String confirmar = modeloPerfil.getConfirmar();
		
		switch(accion) {
		case PERFIL:
			if(hasText(primerNombre)) {
				if (!validaNombre(primerNombre)) {
			    	//El primer nombre contiene caracteres extraños
					errores.addError(new FieldError("modeloPerfil", "primerNombre", fuenteMensajes().getMessage("mensaje.textoInvalido", null, "El texto ingresado contiene caracteres que no corresponden.", locale)));
			    }
			} else {
				//El primer nombre no puede ser nulo o espacios en blanco
				errores.addError(new FieldError("modeloPerfil", "primerNombre", fuenteMensajes().getMessage("mensaje.primerNombreRequerido", null, "Por favor, ingrese el primer nombre.", locale)));
			}
			if(hasText(apellido)) {
				if (!validaNombre(apellido)) {
			    	//El primer nombre contiene caracteres extraños
					errores.addError(new FieldError("modeloPerfil", "apellido", fuenteMensajes().getMessage("mensaje.textoInvalido", null, "El texto ingresado contiene caracteres que no corresponden.", locale)));
			    }
			} else {
				//El apellido no puede ser nulo o espacios en blanco
				errores.addError(new FieldError("modeloPerfil", "apellido", fuenteMensajes().getMessage("mensaje.apellidoRequerido", null, "Por favor, ingrese el apellido.", locale)));
			}
			if(hasText(segundoNombre)) {
				if (!validaNombre(segundoNombre)) {
			    	//El segundo nombre contiene caracteres extraños
					errores.addError(new FieldError("modeloPerfil", "segundoNombre", fuenteMensajes().getMessage("mensaje.textoInvalido", null, "El texto ingresado contiene caracteres que no corresponden.", locale)));
			    }
			}
			if(hasText(correoElectronico)) {
				if(!validaCorreoElectronico(correoElectronico)) {
					//Debe ser una dirección de correo electrónico
					errores.addError(new FieldError("modeloPerfil", "correoElectronico", fuenteMensajes().getMessage("mensaje.correoElectronicoInvalido", null, "Por favor, ingrese un correo electrónico válido.", locale)));
				}
			}
			if(hasText(telefono)) {
				if(!validaTelefono(telefono, locale)) {
					//Debe ser teléfono fijo válido
					errores.addError(new FieldError("modeloPerfil", "telefono", fuenteMensajes().getMessage("mensaje.telefonoFijoInvalido", null, "Por favor, ingrese un número de teléfono fijo válido.", locale)));
				}
			}
			if(hasText(celular)) {
				if(!validaCelular(celular, locale)) {
					//Debe ser teléfono fijo válido
					errores.addError(new FieldError("modeloPerfil", "celular", fuenteMensajes().getMessage("mensaje.telefonoCelularInvalido", null, "Por favor, ingrese un número de teléfono móvil válido.", locale)));
				}
			}
			if(hasText(password) || hasText(confirmar)) {
				if(hasText(password)) {
					if(!validaPassword(password)) {
						//La nueva contraseña no cumple con las reglas
						errores.addError(new FieldError("modeloPerfil", "password", fuenteMensajes().getMessage("mensaje.passwordInvalida", null, "El texto ingresado no cumple con las reglas requeridas.", locale)));
					}
				} else {
					//La nueva contraseña no puede ser nula o espacios en blanco
					errores.addError(new FieldError("modeloPerfil", "password", fuenteMensajes().getMessage("mensaje.passwordRequerida", null, "Por favor, ingrese la contraseña.", locale)));
				}
				if(hasText(confirmar)) {
					if(hasText(password) && !password.equals(confirmar)) {
						//La nueva contraseña y la confirmación no son iguales
						errores.addError(new FieldError("modeloPerfil", "confirmar", fuenteMensajes().getMessage("mensaje.passwordDiferentes", null, "Los textos ingresados no coinciden.", locale)));
					}
				} else {
					//La contraseña debe ser confirmada
					errores.addError(new FieldError("modeloPerfil", "confirmar", fuenteMensajes().getMessage("mensaje.passwordNoConfirmada", null, "Debe confirmar la contraseña.", locale)));
				}
			}
			break;
		default:
			break;
		}
	}
	
	@Override
	public void ejecutarComando(Modelo modelo, BindingResult errores) {
		notNull(modelo, "El modelo no puede ser nulo.");
		isTrue(modelo instanceof ModeloPerfil, "El modelo no es el correcto para este comando.");
		
		ModeloPerfil modeloPerfil = (ModeloPerfil) modelo;
		
		notNull(modeloPerfil.getAccion(), "Accion no puede ser nulo en este modelo.");
		notNull(modeloPerfil.getLocale(), "Locale no puede ser nulo en este modelo.");
		
		Locale locale = modeloPerfil.getLocale();
		Accion accion = modeloPerfil.getAccion();
		
		//CAMPOS NO EDITABLES -> ID
		notNull(modeloPerfil.getId(), "ID no puede ser nulo en este modelo.");
		
		//CAMBIO DE CREDENCIALES
		String password = modeloPerfil.getPassword();
		
		switch (accion) {
		case PERFIL:
			try {
				Usuario usuario = serviciosUsuarios().buscarUsuario(modeloPerfil.getId());
				usuario.setPrimerNombre(modeloPerfil.getPrimerNombre());
				usuario.setSegundoNombre(modeloPerfil.getSegundoNombre());
				usuario.setApellido(modeloPerfil.getApellido());
				usuario.setCorreoElectronico(modeloPerfil.getCorreoElectronico());
				
				String telefono = formatearTelefono(modeloPerfil.getTelefono(), locale);
				String celular = formatearTelefono(modeloPerfil.getCelular(), locale);
				
				usuario.setTelefono(telefono);
				usuario.setCelular(celular);
				
				//Actualizamos el modelo con los nuevos valores de teléfono y celular para reflejar el cambio en la página de respuesta
				modeloPerfil.setTelefono(telefono);
				modeloPerfil.setCelular(celular);
				
				if(hasText(password)) {
					usuario.setPassword(codificadorPassword().encode(password));
				}
				serviciosUsuarios().actualizar(usuario, SERVICIO);
				
			} catch (EntidadNoEncontradaException enee) {
				log.error(enee);
				errores.addError(new ObjectError("modeloPerfil", fuenteMensajes().getMessage("mensaje.datosNoModificados", null, "No hemos podido actualizar los datos. Por favor, contacte al administrador del sistema.", locale)));
			} catch (EntidadNoActualizadaException enae) {
				log.error(enae);
				errores.addError(new ObjectError("modeloPerfil", fuenteMensajes().getMessage("mensaje.datosNoModificados", null, "No hemos podido actualizar los datos. Por favor, contacte al administrador del sistema.", locale)));
			}
			break;
		default:
			break;
		}
	}
	
	/*
	 *  GETTERS
	 */
	public ServiciosUsuarios serviciosUsuarios() {
		return serviciosUsuarios;
	}
	public PasswordEncoder codificadorPassword() {
		return codificadorPassword;
	}

}
